// tp2210.h — portable C library for TP2210 gateway (STM32/ESP32 + FreeRTOS)
// Author: ChatGPT · License: MIT
//
// Compatibility
//  - Requires FreeRTOS headers (semaphores, queues, taskENTER_CRITICAL optional)
//  - No vendor HAL calls inside — you provide UART write + (optional) millisecond tick.
//  - Feed incoming UART bytes to tp2210_input_bytes() from your RX ISR/task.
//
// Features covered from manual V2.1:
//   EUI/VER/REBOOT/DEF, FREQ, PWR, SYMBOL, DEV add/del/query,
//   CHANNEL query/auto/scan, SEND (broadcast & unicast), ATCMD pass-through,
//   TIME set/query, CELLID set/query, ROUTE enable/beacon/delete.
//   Parser emits events for OK/ERROR, +NNMI, DEV ATTACH/REJECT, +DEV (join ask),
//   CDS state, CHANNEL chosen, +ATCMD result, +ROUTECMD result, generic line.
//
// --------------------------- HEADER ---------------------------------
#ifndef TP2210_H
#define TP2210_H

#include <stdint.h>
#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

// ====== Config ======
#ifndef TP2210_TXBUF_SZ
#define TP2210_TXBUF_SZ 256
#endif
#ifndef TP2210_RXLINE_SZ
#define TP2210_RXLINE_SZ 256
#endif
#ifndef TP2210_RESPBUF_SZ
#define TP2210_RESPBUF_SZ 512
#endif
#ifndef TP2210_CMD_TIMEOUT_MS
#define TP2210_CMD_TIMEOUT_MS 1000
#endif

// ====== Opaque handle ======
typedef struct tp2210 tp2210_t;

// ====== Platform hooks you must provide ======
typedef int  (*tp2210_write_fn)(const uint8_t *data, size_t len);           // return bytes written (==len on success)
typedef uint32_t (*tp2210_millis_fn)(void);                                  // milliseconds tick (for timeouts)

// Optional locking (pass NULLs if single-threaded access)
typedef void (*tp2210_lock_fn)(void *user);
typedef void (*tp2210_unlock_fn)(void *user);

// ====== Events from the parser ======
typedef struct {
    // Called when a payload is received: two formats exist per manual.
    // 1) "+NNMI:ESN,len,data" (gateway uplink from a device)
    // 2) "+NNMI:len,data"      (downlink notification to module)
    void (*on_nnmi)(tp2210_t *ctx, const char *esn_opt, uint16_t len, const uint8_t *bytes);

    // Device attach & reject (strings are zero-terminated slices from a copy).
    void (*on_dev_attach)(tp2210_t *ctx, const char *esn, const char *logic_id, int lp_mode, int proto_ver);
    void (*on_dev_reject)(tp2210_t *ctx, const char *esn);

    // Join request line from user-auth mode: "+DEV: ESN,proto,reason,ul_rssi,dl_rssi,txpwr"
    void (*on_dev_request)(tp2210_t *ctx, const char *esn, int proto, int reason, int ul_rssi, int dl_rssi, int txpwr);

    // Channel scan state and auto chosen line
    void (*on_cds_state)(tp2210_t *ctx, int scanning);                       // CDS:1 or CDS:0
    void (*on_channel_chosen)(tp2210_t *ctx, int freq_id, int32_t khz, int noise_dbm);

    // ATCMD/ROUTECMD results bubbled up by gateway
    void (*on_atcmd_result)(tp2210_t *ctx, const char *esn, const char *text_line);
    void (*on_routecmd_result)(tp2210_t *ctx, const char *esn, int cmd, int result);

    // Every unmatched line (for logging)
    void (*on_line)(tp2210_t *ctx, const char *line);
} tp2210_events_t;

// ====== Construction ======
typedef struct {
    tp2210_write_fn  write;          // required
    tp2210_millis_fn millis;         // required
    tp2210_lock_fn   lock;           // optional
    tp2210_unlock_fn unlock;         // optional
    void            *lock_user;      // passed to lock/unlock
    tp2210_events_t  events;         // optional callbacks (any NULL ok)
} tp2210_conf_t;

// Allocate the control block into user-supplied memory for freestanding use.
// "storage" must be at least tp2210_sizeof().
size_t tp2210_sizeof(void);
int    tp2210_init(tp2210_t **out, void *storage, const tp2210_conf_t *conf);

// Feed raw bytes from UART RX (call from ISR or RX task). Returns number of bytes consumed.
//放到串口回调函数里面
size_t tp2210_input_bytes(tp2210_t *ctx, const uint8_t *data, size_t len);

// ====== Synchronous command helpers (send and wait for OK/ERROR or first response line) ======
// Returned value: 0 OK, <0 error/timeout. Optional response buffer receives any lines until OK/ERROR.
int tp2210_cmd(tp2210_t *ctx, uint32_t timeout_ms, char *resp_buf, size_t resp_cap, const char *fmt, ...);

// ====== High-level wrappers ======
int tp2210_get_esn(tp2210_t *ctx, char *out, size_t cap);                    // AT+EUI?
int tp2210_get_ver(tp2210_t *ctx, char *out, size_t cap);                    // AT+VER?
int tp2210_reboot(tp2210_t *ctx);                                            // AT+REBOOT
int tp2210_factory_reset(tp2210_t *ctx);                                     // AT+DEF

int tp2210_freq_get(tp2210_t *ctx, char *out, size_t cap);                   // AT+FREQ?
int tp2210_freq_set(tp2210_t *ctx, int index);                               // AT+FREQ=<idx>

int tp2210_pwr_get(tp2210_t *ctx, int *dbm);                                 // AT+PWR?
int tp2210_pwr_set(tp2210_t *ctx, int dbm);                                  // AT+PWR=0..20

int tp2210_symbol_get(tp2210_t *ctx, char *out, size_t cap);                 // AT+SYMBOL?
int tp2210_symbol_set(tp2210_t *ctx, int dl_idx, int ul_idx);                // AT+SYMBOL=dd,uu or single

int tp2210_dev_query(tp2210_t *ctx, char *out, size_t cap);                  // AT+DEV?
int tp2210_dev_add(tp2210_t *ctx, const char *esn, int lp_mode);             // AT+DEV=0,<ESN>,<mode>
int tp2210_dev_del(tp2210_t *ctx, const char *esn);                          // AT+DEV=1,<ESN>
// User-auth decisions
int tp2210_dev_allow(tp2210_t *ctx, const char *esn, int lp_mode);           // AT+DEV=2,<ESN>,<mode>
int tp2210_dev_reject(tp2210_t *ctx, const char *esn);                       // AT+DEV=3,<ESN>

int tp2210_channel_query(tp2210_t *ctx, char *out, size_t cap);              // AT+CHANNEL?
int tp2210_channel_auto(tp2210_t *ctx, int enable);                          // AT+CHANNEL=0,<0|1>
int tp2210_channel_scan(tp2210_t *ctx);                                      // AT+CHANNEL=1

// Data send. If esn==NULL or esn=="0" => broadcast. Data provided as raw bytes.
int tp2210_send(tp2210_t *ctx, const char *esn_or_zero, const uint8_t *data, size_t len, uint32_t timeout_ms);

// Pass-through AT command to module behind gateway
int tp2210_atcmd_to_node(tp2210_t *ctx, const char *esn, const char *atcmd, uint32_t timeout_ms);

int tp2210_time_get(tp2210_t *ctx, char *out, size_t cap);                   // AT+TIME?
int tp2210_time_set(tp2210_t *ctx, int y,int m,int d,int hh,int mm,int ss);   // AT+TIME=..

int tp2210_cellid_get(tp2210_t *ctx, int *out);                               // AT+CELLID?
int tp2210_cellid_set(tp2210_t *ctx, int id);                                 // AT+CELLID=..

// Route management
int tp2210_route_enable(tp2210_t *ctx, const char *esn, int enable, int ch);  // AT+ROUTE=<ESN>,1,enable,ch
int tp2210_route_beacon(tp2210_t *ctx, const char *esn, int rounds);          // AT+ROUTE=<ESN>,8,rounds
int tp2210_route_delete(tp2210_t *ctx, const char *esn);                      // AT+ROUTE=<ESN>,3

#ifdef __cplusplus
}
#endif
#endif // TP2210_H


//----------------------------------------使用方法如下-------------------------------------

// #include "tp2210.h"
// #include "usart.h"     // CubeMX 自动生成的 UART HAL 库
// #include "cmsis_os.h"
// #include <stdio.h>
//
// // 全局 LoRa 对象
// static tp2210_t tp;
// static uint8_t tp_mem[512];
//
// // UART 写函数
// static int uart_write(const uint8_t *data, size_t len) {
//     HAL_UART_Transmit(&huart1, (uint8_t*)data, len, 100);
//     return len;
// }
//
// // 毫秒计数
// static uint32_t my_millis(void) {
//     return (uint32_t)osKernelGetTickCount();
// }
//
// // 收到下行消息回调
// static void on_nnmi(tp2210_t *ctx, const char *esn, uint16_t len, const uint8_t *bytes) {
//     printf("STM32 收到下行 %u 字节数据\n", len);
// }
//
// // === UART 接收中断回调 ===
// void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
//     if (huart->Instance == USART1) {
//         // 把接收到的字节交给 tp2210
//         tp2210_input_bytes(&tp, &tp.rx_byte, 1);
//         // 继续接收下一个字节
//         HAL_UART_Receive_IT(&huart1, &tp.rx_byte, 1);
//     }
// }
//
// // === LoRa 任务 ===
// void tp_task(void *argument) {
//     // 初始化 tp2210
//     tp2210_conf_t conf = {
//         .write  = uart_write,
//         .millis = my_millis,
//         .events = {
//             .on_nnmi = on_nnmi,
//         },
//     };
//     tp2210_init(&tp, tp_mem, &conf);
//
//     // 启动 UART 接收中断
//     HAL_UART_Receive_IT(&huart1, &tp.rx_byte, 1);
//
//     // 发送 AT 命令：获取 ESN
//     tp2210_get_esn(tp, buf, sizeof(buf));
//     tp.wait_result = 1;   // 等待 OK/ERROR
//
//     for (;;) {
//         // 检查 wait_result 来驱动逻辑
//         switch (tp.wait_result) {
//             case 0: // 成功
//                 // 发一条广播
//                 uint8_t hello[5] = {1,2,3,4,5};
//                 tp2210_send(g_tp, "0", hello, 5, 2000);
//                 tp.wait_result = 1;
//                 break;
//
//             case -1: // ERROR
//                 uint8_t hello[5] = {1,2,3,4,5};
//                 tp2210_send(g_tp, "0", hello, 5, 2000);
//                 tp.wait_result = 1;
//                 break;
//
//             case -2: // 超时
//                 uint8_t hello[5] = {1,2,3,4,5};
//                 tp2210_send(g_tp, "0", hello, 5, 2000);
//                 tp.wait_result = 1;
//                 break;
//
//             default:
//                 // 空闲或等待
//                 break;
//         }
//
//         osDelay(1000); // 任务周期检查
//     }
// }
// //ESP32 FreeRTOS 示例
// #include "tp2210.h"
// #include "driver/uart.h"
// #include "freertos/FreeRTOS.h"
// #include "freertos/task.h"
//
// static tp2210_t *g_tp;
// static uint8_t   tp_mem[512];
//
// static int uart_write(const uint8_t *data, size_t len) {
//     return uart_write_bytes(UART_NUM_1, (const char*)data, len);
// }
// static uint32_t my_millis(void) {
//     return (uint32_t)(xTaskGetTickCount() * portTICK_PERIOD_MS);
// }
//
// static void on_nnmi(tp2210_t *ctx, const char *esn, uint16_t len, const uint8_t *bytes) {
//     printf("ESP32 收到 %u 字节数据\n", len);
// }
//
// void tp_rx_task(void *arg) {
//     uint8_t buf[128];
//     for (;;) {
//         int n = uart_read_bytes(UART_NUM_1, buf, sizeof(buf), pdMS_TO_TICKS(50));
//         if (n > 0) tp2210_input_bytes(g_tp, buf, n);
//     }
// }
//
// void tp_app_task(void *arg) {
//     tp2210_conf_t conf = {
//         .write = uart_write,
//         .millis = my_millis,
//         .events = {
//             .on_nnmi = on_nnmi,
//         },
//     };
//     tp2210_init(&g_tp, tp_mem, &conf);
//
//     char buf[64];
//     tp2210_get_esn(g_tp, buf, sizeof(buf));
//     printf("网关ESN: %s\n", buf);
//
//     uint8_t hello[5] = {'H','e','l','l','o'};
//     tp2210_send(g_tp, "0", hello, 5, 2000);
//
//     vTaskDelete(NULL);
// }
//
// void app_main(void) {
//     uart_config_t cfg = {
//         .baud_rate = 115200,
//         .data_bits = UART_DATA_8_BITS,
//         .parity    = UART_PARITY_DISABLE,
//         .stop_bits = UART_STOP_BITS_1,
//         .flow_ctrl = UART_HW_FLOWCTRL_DISABLE
//     };
//     uart_driver_install(UART_NUM_1, 2048, 0, 0, NULL, 0);
//     uart_param_config(UART_NUM_1, &cfg);
//     uart_set_pin(UART_NUM_1, 17, 16, -1, -1); // TX=17, RX=16
//
//     xTaskCreate(tp_rx_task, "tp_rx", 2048, NULL, 10, NULL);
//     xTaskCreate(tp_app_task,"tp_app",4096, NULL, 9, NULL);
// }



// --------------------------- EXAMPLES esp32 stm32 ---------------------------------
// Minimal examples for STM32(HAL)+FreeRTOS and ESP32(IDF)+FreeRTOS.
// Build tips:
//  - Define TP2210_IMPLEMENTATION in exactly one C file before including tp2210.h
//  - Ensure your UART RX task feeds bytes into tp2210_input_bytes().
//
// File: app_tp2210_example.c
#if 0
#include "tp2210.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"

// ---- Platform glue (example for both STM32 and ESP32) ----
static int uart_write_portable(const uint8_t *data, size_t len){
#ifdef ESP_PLATFORM
    // ESP32: use uart_write_bytes(UART_NUM_1,...)
    int written = uart_write_bytes(UART_NUM_1, (const char*)data, len);
    return written;
#else
    // STM32 HAL (blocking as example)
    HAL_UART_Transmit(&huart1, (uint8_t*)data, len, 100);
    return (int)len;
#endif
}
static uint32_t my_millis(void){ return (uint32_t)(xTaskGetTickCount()* portTICK_PERIOD_MS); }

static tp2210_t *g_tp;
static uint8_t   tp_mem[512];

static void on_nnmi(tp2210_t *ctx, const char *esn, uint16_t len, const uint8_t *bytes){
    // Print/log payload
#ifdef ESP_PLATFORM
    printf("NNMI from %s len=%u\n", esn?esn:"(broadcast)", len);
#else
    // STM32: minimal printf
    printf("NNMI len=%u\n", len);
#endif
}

static void uart_rx_task(void *arg){
    uint8_t buf[128];
    for(;;){
#ifdef ESP_PLATFORM
        int n = uart_read_bytes(UART_NUM_1, buf, sizeof(buf), pdMS_TO_TICKS(50));
#else
        // STM32: poll or use DMA+ring then copy out. Here we fake via blocking receive timeout 50ms
        int n = 0; // replace with your ring buffer read
#endif
        if(n>0) tp2210_input_bytes(g_tp, buf, (size_t)n);
    }
}

static void app_main_task(void *arg){
    tp2210_conf_t conf = {
        .write  = uart_write_portable,
        .millis = my_millis,
        .events = {
            .on_nnmi = on_nnmi,
        },
    };
    tp2210_init(&g_tp, tp_mem, &conf);

    // Query ESN & version
    char resp[128];
    if(tp2210_get_esn(g_tp, resp, sizeof(resp))==0) printf("EUI?\n%s\n", resp);
    if(tp2210_get_ver(g_tp, resp, sizeof(resp))==0) printf("VER?\n%s\n", resp);

    // Set air rates (DL=19.2, UL=9.6) and frequency index 0
    tp2210_symbol_set(g_tp, 3, 2);
    tp2210_freq_set(g_tp, 0);

    // Send broadcast 5 bytes
    const uint8_t hello[5] = {0x01,0x02,0x03,0x04,0x05};
    tp2210_send(g_tp, "0", hello, sizeof(hello), 2000);

    // Example: add a device and unicast to it
    tp2210_dev_add(g_tp, "FF0100001111", 3);  // TypeD
    const uint8_t data2[] = {0x00,0x11,0x22,0x33,0x44};
    tp2210_send(g_tp, "FF0100001111", data2, sizeof(data2), 2000);

    // Loop forever (work done in RX task callbacks)
    for(;;){ vTaskDelay(pdMS_TO_TICKS(1000)); }
}

void app_start(void){
    xTaskCreate(uart_rx_task,   "tp_rx", 2048, NULL, 10, NULL);
    xTaskCreate(app_main_task,  "tp_app", 4096, NULL, 9,  NULL);
}
#endif

// ---------------- Receiver-only tiny example ----------------
#if 0
// Registers only on_nnmi and prints payload length. Useful to act as gateway listener.
static void on_nnmi(tp2210_t *ctx, const char *esn, uint16_t len, const uint8_t *bytes){
    printf("RX %u bytes from %s\n", len, esn?esn:"?");
}
#endif

// ---------------- Sender-only tiny example ----------------
#if 0
void tp_send_once(tp2210_t *tp){
    const uint8_t payload[] = { 'H','e','l','l','o' };
    tp2210_send(tp, "0", payload, sizeof(payload), 2000); // broadcast
}
#endif
